/**
 * @class UI组件-场景切换加载页
 * @author DuskyHuang 圣子
*/

import { _decorator, UITransform, director, ProgressBar, Label, Node } from 'cc';
import { __cfw_notifications__ } from '../configs';
import { inner, notifier, managers } from '../core/boot';
import { group } from '../utils';
import BaseUI from './base';
const { ccclass } = _decorator;

@ccclass('cfw.LoadingPage')
export class LoadingPage extends BaseUI {

    @group("UI", "进度条", ProgressBar) public bar: ProgressBar;
    @group("UI", "进度文本", Label) public label: Label;
    @group("UI", "文本模板（%为百分比）") public chars: `${string}${'%'}${string}` = "%";
    @group("UI", "进度游标", Node) public cruiser: Node;

    @group("Logic", "加载完毕通知") public completeNotification: string = "scene loaded";
    @group("Logic", "加载过程通知") public progressNotification: string = "scene progress";
    @group("Logic", "完毕自动隐藏") public autoHide: boolean = true;

    protected onLoad(): void { inner.dispatch(__cfw_notifications__.UI.REGISTER, this); }
    protected onDestroy(): void { inner.dispatch(__cfw_notifications__.UI.UNREGISTER, this); }

    private _group: number = 0;
    private _groupsNum: number = 1;

    public async loadEnter(...scenes: string[]): Promise<boolean> {
        if (!notEmptyArr(scenes)) return true;
        return await this._loadOneByOne(scenes, true);
    }

    public async loadReady(...scenes: string[]): Promise<boolean> {
        if (!notEmptyArr(scenes)) return true;
        return await this._loadOneByOne(scenes, false);
    }

    private async _loadOneByOne(scenes: string[], autoEnter: boolean): Promise<boolean> {
        this._group = 0;
        this._groupsNum = scenes.length;
        for (let scene of scenes) {
            if (!scene) { console.warn("[LoadingPage.load]", "try to load scene empty name"); return false; }
            if (managers.scene.hasLoaded(scene)) {
                if (!this._isLast) continue;
                return this._lastFinished(autoEnter ? scenes.head : null);
            } else {
                this.show();
                await this._preloadOneScene(scene);
                managers.scene.addLoaded(scene);
                if (this._isLast) {
                    return this._lastFinished(autoEnter ? scenes.head : null);
                }
            }
            this._group++;
        }
    }

    private get _isLast(): boolean { return this._group >= this._groupsNum - 1; }

    private async _preloadOneScene(name: string): Promise<string> {
        return new Promise(resolve => {
            const floor = this._group / this._groupsNum;
            director.preloadScene(name, (count: num.int, total: num.int) => {
                let pct: num.pct = count / total / this._groupsNum;
                pct += floor;
                // 避免cocos的回参totalCount发生变化出现回溯
                if (pct > this.bar.progress) this.setProgress(pct);
            }, () => resolve(name));
        });
    }

    public setProgress(pct: num.pct): this {
        const { bar, label, chars, cruiser } = this;
        const width = this.bar?.getComponent(UITransform)?.width ?? 0;
        if (!!bar) bar.progress = pct;
        if (!!label) label.string = chars.replace('%', `${Math.round(pct * 100)}%`);
        if (!!cruiser) cruiser.setPosition((pct - 0.5) * width, cruiser.position.y);
        if (!!this.progressNotification) notifier.dispatch(this.progressNotification, pct);
        return this;
    }

    private _lastFinished(enterScene?: string): boolean {
        if (!enterScene) {
            if (!!this.completeNotification) notifier.dispatch(this.completeNotification);
            if (!!this.autoHide && this.node) this.hide();
        } else {
            director.loadScene(enterScene, () => {
                managers.scene.pushRecord(enterScene);
                if (!!this.completeNotification) notifier.dispatch(this.completeNotification);
                if (!!this.autoHide && this.node) this.hide();
            });
        }
        return true;
    }
}